﻿# Notepad-like editor implemented with built-in PowerShell + .NET WinForms (no external dependencies)
# Save as NotepadClone.ps1 and run with: powershell -NoProfile -ExecutionPolicy Bypass -STA -File .\NotepadClone.ps1

# Ensure STA apartment and robust error policy
if ([System.Threading.Thread]::CurrentThread.ApartmentState -ne [System.Threading.ApartmentState]::STA) {
    try { $exe = (Get-Process -Id $PID).Path } catch { $exe = 'powershell.exe' }
    $argsList = @('-NoProfile','-ExecutionPolicy','Bypass','-STA','-File',"$PSCommandPath")
    Start-Process -FilePath $exe -ArgumentList $argsList -WindowStyle Normal
    exit
}
$ErrorActionPreference = 'Stop'
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName Microsoft.VisualBasic
Add-Type -AssemblyName System.Web

# ---------------- State ----------------
$script:WordWrapEnabled = $true
$script:ShowStatusBar = $true
$script:ApiKey = 'sk-d225ff50182f4a55adc545c1c472b7ee'
$script:IsAIProcessing = $false

# ---------------- Helpers ----------------
function Update-StatusBar {
    param($tb, $statusLabel)
    if (-not $script:ShowStatusBar -or -not $tb -or -not $statusLabel) { return }

    try {
        # 防御性读取，避免空/未初始化阶段的异常
        $selStart = [int]0
        if ($tb -and $null -ne $tb.SelectionStart) { $selStart = [int]$tb.SelectionStart }

        $line = $tb.GetLineFromCharIndex([int]$selStart)
        if ($line -lt 0) { $line = 0 }

        $firstCharOfLine = $tb.GetFirstCharIndexFromLine([int]$line)
        if ($firstCharOfLine -lt 0) { $firstCharOfLine = 0 }

        $col = ($selStart - $firstCharOfLine)
        if ($col -lt 0) { $col = 0 }

        $length = 0
        if ($tb -and $null -ne $tb.TextLength) { $length = [int]$tb.TextLength }

        $lines = $tb.Lines
        $linesCount = if ($lines) { [int]$lines.Length } else { 0 }

        # 添加加载动画
        $loadingIndicator = if ($script:IsAIProcessing) { " ⏳" } else { "" }
        $statusLabel.Text = ("行: {0} 列: {1}    长度: {2}    行数: {3}{4}" -f ([int]$line + 1), ([int]$col + 1), [int]$length, [int]$linesCount, $loadingIndicator)
    } catch {
        # 出现异常时不抛出，保证 UI 流畅
        return
    }
}

function Update-TabTitle {
    param([System.Windows.Forms.TabPage]$tab, $doc)
    if (-not $tab -or -not $doc) { return }
    $displayName = if ([string]::IsNullOrWhiteSpace($doc.FilePath)) {
        if ($doc.Title) { $doc.Title } else { '无标题' }
    } else {
        [IO.Path]::GetFileName($doc.FilePath)
    }
    $modifiedMark = if ($doc.IsModified) { "*" } else { "" }
    $tab.Text = "$displayName$modifiedMark ×"
}

function Set-FormTitle {
    param([System.Windows.Forms.Form]$form, [System.Windows.Forms.TabControl]$tabs)
    if (-not $form -or -not $tabs) { return }
    $tab = $tabs.SelectedTab
    if (-not $tab) { $form.Text = "记事本"; return }
    $doc = $tab.Tag
    $name = if ([string]::IsNullOrWhiteSpace($doc.FilePath)) {
        if ($doc.Title) { $doc.Title } else { "无标题" }
    } else { [IO.Path]::GetFileName($doc.FilePath) }
    $modifiedMark = if ($doc.IsModified) { "*" } else { "" }
    $form.Text = "记事本 - $name$modifiedMark"
}

function Detect-And-ReadText {
    param([string]$path, [ref]$encodingOut)
    $bytes = [System.IO.File]::ReadAllBytes($path)
    if ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) {
        $encodingOut.Value = [System.Text.UTF8Encoding]::new($true)
        return [System.Text.Encoding]::UTF8.GetString($bytes, 3, $bytes.Length - 3)
    } elseif ($bytes.Length -ge 2 -and $bytes[0] -eq 0xFF -and $bytes[1] -eq 0xFE) {
        $encodingOut.Value = [System.Text.Encoding]::Unicode  # UTF-16 LE
        return [System.Text.Encoding]::Unicode.GetString($bytes, 2, $bytes.Length - 2)
    } elseif ($bytes.Length -ge 2 -and $bytes[0] -eq 0xFE -and $bytes[1] -eq 0xFF) {
        $encodingOut.Value = [System.Text.Encoding]::BigEndianUnicode  # UTF-16 BE
        return [System.Text.Encoding]::BigEndianUnicode.GetString($bytes, 2, $bytes.Length - 2)
    } else {
        $encodingOut.Value = [System.Text.Encoding]::Default
        return [System.Text.Encoding]::Default.GetString($bytes)
    }
}

function Write-TextToFile {
    param([string]$path, [string]$text, [System.Text.Encoding]$encoding)
    $bytes = $encoding.GetBytes($text)
    [System.IO.File]::WriteAllBytes($path, $bytes)
}

function Get-CurrentDoc {
    param([System.Windows.Forms.TabControl]$tabs)
    if (-not $tabs.SelectedTab) { return $null }
    return $tabs.SelectedTab.Tag
}

function Format-ReplyText {
    param(
        [string]$text,
        $originalDoc
    )
    
    try {
        if ([string]::IsNullOrEmpty($text)) {
            return ""
        }
        
        # 移除HTML标签
        $text = $text -replace '<[^>]+>', ''
        
        # 统一换行符为Windows标准
        $text = $text -replace '\r?\n', "`r`n"
        
        # 格式化代码块 - 为SQL、代码等添加缩进
        $lines = $text -split "`r`n"
        $formattedLines = @()
        $inCodeBlock = $false
        
        foreach ($line in $lines) {
            $trimmedLine = $line.Trim()
            
            # 检测代码块开始（SQL语句、代码等）
            if ($trimmedLine -match '^(CREATE|INSERT|SELECT|UPDATE|DELETE|ALTER|DROP|SHOW|DESCRIBE|EXPLAIN)\s+' -or 
                $trimmedLine -match '^[A-Z_]+\s*=' -or
                $trimmedLine -match '^[a-zA-Z_][a-zA-Z0-9_]*\s*\(' -or
                $trimmedLine -match '^\s*[{}]' -or
                $trimmedLine -match '^\s*//' -or
                $trimmedLine -match '^\s*#' -or
                $trimmedLine -match '^\s*--') {
                $inCodeBlock = $true
                $formattedLines += "    " + $trimmedLine
            }
            # 检测代码块结束（空行或非代码行）
            elseif ($trimmedLine -eq '' -or $trimmedLine -match '^[^A-Za-z0-9_\s]') {
                if ($inCodeBlock -and $trimmedLine -eq '') {
                    $formattedLines += ""
                    $inCodeBlock = $false
                } else {
                    $formattedLines += $trimmedLine
                }
            }
            # 代码块内的续行
            elseif ($inCodeBlock) {
                $formattedLines += "    " + $trimmedLine
            }
            # 普通文本
            else {
                $formattedLines += $trimmedLine
            }
        }
        
        # 重新组合文本
        $text = $formattedLines -join "`r`n"
        
        # 清理多余的空行（最多保留两个连续空行）
        $text = $text -replace '(\r\n){3,}', "`r`n`r`n"
        
        # 移除首尾空白
        $text = $text.Trim()
        
        return $text
    } catch {
        return $text
    }
}

# ---------------- UI ----------------
$form = New-Object System.Windows.Forms.Form
$form.StartPosition = 'CenterScreen'
$form.Size = New-Object System.Drawing.Size(900, 600)
$form.MinimumSize = New-Object System.Drawing.Size(400, 300)

$menu = New-Object System.Windows.Forms.MenuStrip
$form.MainMenuStrip = $menu

$miFile   = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '文件(&F)' }
$miEdit   = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '编辑(&E)' }
$miFormat = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '格式(&O)' }
$miView   = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '查看(&V)' }
$miSettings = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '设置(&S)' }
$miHelp   = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '帮助(&H)' }

# File menu items
$miNew      = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '新建(&N)'; ShortcutKeys = [System.Windows.Forms.Keys]::Control -bor [System.Windows.Forms.Keys]::N }
$miOpen     = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '打开(&O)'; ShortcutKeys = [System.Windows.Forms.Keys]::Control -bor [System.Windows.Forms.Keys]::O }
$miSave     = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '保存(&S)'; ShortcutKeys = [System.Windows.Forms.Keys]::Control -bor [System.Windows.Forms.Keys]::S }
$miSaveAs   = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '另存为(&A)...' }
$miClose    = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '关闭(&C)'; ShortcutKeys = [System.Windows.Forms.Keys]::Control -bor [System.Windows.Forms.Keys]::W }
$miExit     = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '退出(&X)' }

# Edit menu items
$miUndo     = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '撤销(&U)'; ShortcutKeys = [System.Windows.Forms.Keys]::Control -bor [System.Windows.Forms.Keys]::Z }
$miCut      = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '剪切(&T)'; ShortcutKeys = [System.Windows.Forms.Keys]::Control -bor [System.Windows.Forms.Keys]::X }
$miCopy     = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '复制(&C)'; ShortcutKeys = [System.Windows.Forms.Keys]::Control -bor [System.Windows.Forms.Keys]::C }
$miPaste    = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '粘贴(&P)'; ShortcutKeys = [System.Windows.Forms.Keys]::Control -bor [System.Windows.Forms.Keys]::V }
$miDelete   = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '删除(&L)'; ShortcutKeys = [System.Windows.Forms.Keys]::Delete }
$miSelectAll= New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '全选(&A)'; ShortcutKeys = [System.Windows.Forms.Keys]::Control -bor [System.Windows.Forms.Keys]::A }
$miTimeDate = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '时间/日期(&D)'; ShortcutKeys = [System.Windows.Forms.Keys]::F5 }

# Format menu items
$miWordWrap = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '自动换行(&W)'; Checked = $true; CheckOnClick = $true }
$miFont     = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '字体(&F)...' }

# View menu items
$miStatusBar= New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '状态栏(&S)'; Checked = $true; CheckOnClick = $true }
$miHtmlPreview = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = 'HTML预览(&H)'; ShortcutKeys = [System.Windows.Forms.Keys]::Control -bor [System.Windows.Forms.Keys]::F12 }

# Settings menu items
$miApiKey   = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = 'API 密钥设置(&K)...' }

# Help menu items
$miAbout    = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '关于记事本(&A)' }
$miAI       = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = 'AI 处理(&D)'; ShortcutKeys = [System.Windows.Forms.Keys]::Control -bor [System.Windows.Forms.Keys]::D }

$miFile.DropDownItems.AddRange(@($miNew,$miOpen,$miSave,$miSaveAs,(New-Object System.Windows.Forms.ToolStripSeparator),$miClose,(New-Object System.Windows.Forms.ToolStripSeparator),$miExit))
$miEdit.DropDownItems.AddRange(@($miUndo,(New-Object System.Windows.Forms.ToolStripSeparator),$miCut,$miCopy,$miPaste,$miDelete,(New-Object System.Windows.Forms.ToolStripSeparator),$miSelectAll,$miTimeDate))
$miFormat.DropDownItems.AddRange(@($miWordWrap,$miFont))
$miView.DropDownItems.AddRange(@($miStatusBar,$miHtmlPreview))
$miSettings.DropDownItems.AddRange(@($miApiKey))
$miHelp.DropDownItems.AddRange(@($miAbout,$miAI))

$menu.Items.AddRange(@($miFile,$miEdit,$miFormat,$miView,$miSettings,$miHelp))
$form.Controls.Add($menu)

# Tab control (multi-document)
$tabs = New-Object System.Windows.Forms.TabControl
$tabs.Dock = 'Fill'
$form.Controls.Add($tabs)
$tabs.BringToFront()

# Tab context menu
$tabContextMenu = New-Object System.Windows.Forms.ContextMenuStrip
$miCloseTab = New-Object System.Windows.Forms.ToolStripMenuItem -Property @{ Text = '关闭(&C)' }
$miCloseTab.add_Click({ Close-CurrentDocument })
$tabContextMenu.Items.Add($miCloseTab) | Out-Null

# Tab click event for close button
$tabs.add_MouseClick({
    param($sender, $e)
    try {
        if ($e.Button -eq [System.Windows.Forms.MouseButtons]::Left) {
            for ($i = 0; $i -lt $sender.TabPages.Count; $i++) {
                $rect = $sender.GetTabRect($i)
                $tabText = $sender.TabPages[$i].Text
                
                # 检查是否点击了关闭按钮区域（×符号附近）
                if ($tabText.EndsWith(" ×")) {
                    $textSize = $sender.CreateGraphics().MeasureString($tabText, $sender.Font)
                    $closeButtonX = $rect.X + $textSize.Width - 10
                    $closeButtonRect = New-Object System.Drawing.Rectangle($closeButtonX, $rect.Y, 15, $rect.Height)
                    
                    if ($closeButtonRect.Contains($e.Location)) {
                        $sender.SelectedIndex = $i
                        Close-CurrentDocument
                        break
                    }
                }
            }
        }
    } catch {
        # 忽略错误
    }
})

# Status bar
$status = New-Object System.Windows.Forms.StatusStrip
$statusLabel = New-Object System.Windows.Forms.ToolStripStatusLabel -Property @{ Text = '就绪' }
$status.Items.Add($statusLabel) | Out-Null
$form.Controls.Add($status)

# Dialogs
$ofd = New-Object System.Windows.Forms.OpenFileDialog -Property @{ Filter = '文本文件|*.txt;*.log;*.md;*.csv;*.ps1;*.json;*.xml|所有文件|*.*'; Multiselect = $false }
$sfd = New-Object System.Windows.Forms.SaveFileDialog -Property @{ Filter = '文本文件|*.txt|所有文件|*.*'; AddExtension = $true; DefaultExt = 'txt' }
$fontDialog = New-Object System.Windows.Forms.FontDialog

# ---------------- Doc/Tab operations ----------------
function New-Tab {
    param(
        [string]$title = '无标题',
        [string]$text = '',
        [string]$path = $null,
        [System.Text.Encoding]$encoding = [System.Text.Encoding]::UTF8
    )

    $tab = New-Object System.Windows.Forms.TabPage
    $tb = New-Object System.Windows.Forms.TextBox
    $tb.Multiline = $true
    $tb.AcceptsTab = $true
    $tb.ScrollBars = if ($script:WordWrapEnabled) { 'Vertical' } else { 'Both' }
    $tb.WordWrap = $script:WordWrapEnabled
    $tb.Dock = 'Fill'
    $tb.Font = New-Object System.Drawing.Font('Consolas', 12)
    $tb.BorderStyle = 'None'
    $tb.Text = $text

    $doc = [pscustomobject]@{
        Title     = $title
        FilePath  = $path
        Encoding  = $encoding
        IsModified= $false
        TextBox   = $tb
    }
    $tab.Tag = $doc
    Update-TabTitle -tab $tab -doc $doc

    # Text events
    $tb.add_TextChanged({
        $d = $tab.Tag
        if ($d) {
            $d.IsModified = $true
            Update-TabTitle -tab $tab -doc $d
            Set-FormTitle -form $form -tabs $tabs
        }
        Update-StatusBar -tb $tb -statusLabel $statusLabel
    })
    $tb.add_KeyUp({ Update-StatusBar -tb $tb -statusLabel $statusLabel })
    $tb.add_MouseUp({ Update-StatusBar -tb $tb -statusLabel $statusLabel })

    $tab.Controls.Add($tb)
    [void]$tabs.TabPages.Add($tab)
    $tabs.SelectedTab = $tab
    
    # Add context menu to tab
    $tab.ContextMenuStrip = $tabContextMenu
    
    Update-StatusBar -tb $tb -statusLabel $statusLabel
    Set-FormTitle -form $form -tabs $tabs
}

function Confirm-DiscardChanges {
    param($doc)
    if (-not $doc -or -not $doc.IsModified) { return $true }
    $result = [System.Windows.Forms.MessageBox]::Show(
        "是否保存对文档的更改？",
        "记事本",
        [System.Windows.Forms.MessageBoxButtons]::YesNoCancel,
        [System.Windows.Forms.MessageBoxIcon]::Question
    )
    switch ($result) {
        'Yes'    { if (Save-File) { return $true } else { return $false } }
        'No'     { return $true }
        'Cancel' { return $false }
    }
}

function New-File {
    $doc = Get-CurrentDoc -tabs $tabs
    if ($doc -and -not (Confirm-DiscardChanges -doc $doc)) { return }
    New-Tab -title '无标题' -text '' -path $null -encoding ([System.Text.Encoding]::UTF8)
}

function Open-File {
    $doc = Get-CurrentDoc -tabs $tabs
    if ($doc -and -not (Confirm-DiscardChanges -doc $doc)) { return }
    if ($ofd.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
        try {
            $encRef = [ref]([System.Text.Encoding]::UTF8)
            $text = Detect-And-ReadText -path $ofd.FileName -encodingOut $encRef
            New-Tab -title ([IO.Path]::GetFileName($ofd.FileName)) -text $text -path $ofd.FileName -encoding $encRef.Value
        } catch {
            [System.Windows.Forms.MessageBox]::Show("无法打开文件: $($_.Exception.Message)", '错误', 'OK', '错误') | Out-Null
        }
    }
}

function Save-File {
    $doc = Get-CurrentDoc -tabs $tabs
    if (-not $doc) { return $false }
    if ([string]::IsNullOrWhiteSpace($doc.FilePath)) { return SaveAs-File }
    try {
        Write-TextToFile -path $doc.FilePath -text $doc.TextBox.Text -encoding $doc.Encoding
        $doc.IsModified = $false
        Update-TabTitle -tab $tabs.SelectedTab -doc $doc
        Set-FormTitle -form $form -tabs $tabs
        return $true
    } catch {
        [System.Windows.Forms.MessageBox]::Show("保存失败: $($_.Exception.Message)", '错误', 'OK', '错误') | Out-Null
        return $false
    }
}

function SaveAs-File {
    $doc = Get-CurrentDoc -tabs $tabs
    if (-not $doc) { return $false }
    if ($sfd.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) {
        try {
            $encChoice = [System.Windows.Forms.MessageBox]::Show('使用 UTF-8 编码保存？选择"否"为本地(ANSI)编码。', '选择编码', 'YesNo', 'Question')
            if ($encChoice -eq [System.Windows.Forms.DialogResult]::Yes) {
                $doc.Encoding = [System.Text.UTF8Encoding]::new($false) # UTF-8 without BOM
            } else {
                $doc.Encoding = [System.Text.Encoding]::Default
            }
            Write-TextToFile -path $sfd.FileName -text $doc.TextBox.Text -encoding $doc.Encoding
            $doc.FilePath = $sfd.FileName
            $doc.IsModified = $false
            $doc.Title = [IO.Path]::GetFileName($doc.FilePath)
            Update-TabTitle -tab $tabs.SelectedTab -doc $doc
            Set-FormTitle -form $form -tabs $tabs
            return $true
        } catch {
            [System.Windows.Forms.MessageBox]::Show("保存失败: $($_.Exception.Message)", '错误', 'OK', '错误') | Out-Null
            return $false
        }
    }
    return $false
}

function Close-CurrentDocument {
    $doc = Get-CurrentDoc -tabs $tabs
    if (-not $doc) { return }
    
    if (-not (Confirm-DiscardChanges -doc $doc)) { return }
    
    $currentTab = $tabs.SelectedTab
    if ($currentTab) {
        [void]$tabs.TabPages.Remove($currentTab)
        
        # 如果关闭后没有标签页了，创建一个新的
        if ($tabs.TabPages.Count -eq 0) {
            New-Tab -title '无标题' -text '' -path $null -encoding ([System.Text.Encoding]::UTF8)
        } else {
            # 更新状态栏和标题
            $newDoc = Get-CurrentDoc -tabs $tabs
            if ($newDoc -and $newDoc.TextBox) {
                Update-StatusBar -tb $newDoc.TextBox -statusLabel $statusLabel
            }
            Set-FormTitle -form $form -tabs $tabs
        }
    }
}

function Show-ApiKeySettings {
    $settingsForm = New-Object System.Windows.Forms.Form
    $settingsForm.Text = 'API 密钥设置'
    $settingsForm.Size = New-Object System.Drawing.Size(500, 200)
    $settingsForm.StartPosition = 'CenterParent'
    $settingsForm.FormBorderStyle = 'FixedDialog'
    $settingsForm.MaximizeBox = $false
    $settingsForm.MinimizeBox = $false
    
    $label = New-Object System.Windows.Forms.Label
    $label.Text = 'DeepSeek API 密钥:'
    $label.Location = New-Object System.Drawing.Point(20, 30)
    $label.Size = New-Object System.Drawing.Size(120, 20)
    $settingsForm.Controls.Add($label)
    
    $textBox = New-Object System.Windows.Forms.TextBox
    $textBox.Text = $script:ApiKey
    $textBox.Location = New-Object System.Drawing.Point(150, 28)
    $textBox.Size = New-Object System.Drawing.Size(300, 20)
    $textBox.UseSystemPasswordChar = $true
    $settingsForm.Controls.Add($textBox)
    
    $btnOK = New-Object System.Windows.Forms.Button
    $btnOK.Text = '确定'
    $btnOK.Location = New-Object System.Drawing.Point(300, 80)
    $btnOK.Size = New-Object System.Drawing.Size(75, 25)
    $btnOK.DialogResult = [System.Windows.Forms.DialogResult]::OK
    $settingsForm.Controls.Add($btnOK)
    
    $btnCancel = New-Object System.Windows.Forms.Button
    $btnCancel.Text = '取消'
    $btnCancel.Location = New-Object System.Drawing.Point(380, 80)
    $btnCancel.Size = New-Object System.Drawing.Size(75, 25)
    $btnCancel.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
    $settingsForm.Controls.Add($btnCancel)
    
    $btnOK.add_Click({
        if (-not [string]::IsNullOrWhiteSpace($textBox.Text)) {
            $script:ApiKey = $textBox.Text
            $env:DEEPSEEK_API_KEY = $textBox.Text
        }
        $settingsForm.Close()
    })
    
    $settingsForm.ShowDialog($form) | Out-Null
}

function Show-HtmlPreview {
    $doc = Get-CurrentDoc -tabs $tabs
    if (-not $doc) {
        [System.Windows.Forms.MessageBox]::Show("没有可预览的文档", '提示', 'OK', 'Information') | Out-Null
        return
    }
    
    $htmlContent = $doc.TextBox.Text
    if ([string]::IsNullOrWhiteSpace($htmlContent)) {
        [System.Windows.Forms.MessageBox]::Show("当前文档为空", '提示', 'OK', 'Information') | Out-Null
        return
    }
    
    # 创建HTML预览窗口
    $previewForm = New-Object System.Windows.Forms.Form
    $previewForm.Text = 'HTML预览'
    $previewForm.Size = New-Object System.Drawing.Size(800, 600)
    $previewForm.StartPosition = 'CenterParent'
    $previewForm.MinimumSize = New-Object System.Drawing.Size(400, 300)
    
    # 创建工具栏
    $toolStrip = New-Object System.Windows.Forms.ToolStrip
    $toolStrip.Dock = 'Top'
    
    # 刷新按钮
    $btnRefresh = New-Object System.Windows.Forms.ToolStripButton
    $btnRefresh.Text = '刷新'
    $btnRefresh.ToolTipText = '刷新预览'
    $toolStrip.Items.Add($btnRefresh) | Out-Null
    
    # 在新窗口打开按钮
    $btnOpenInBrowser = New-Object System.Windows.Forms.ToolStripButton
    $btnOpenInBrowser.Text = '在浏览器中打开'
    $btnOpenInBrowser.ToolTipText = '在默认浏览器中打开'
    $toolStrip.Items.Add($btnOpenInBrowser) | Out-Null
    
    $toolStrip.Items.Add((New-Object System.Windows.Forms.ToolStripSeparator)) | Out-Null
    
    # 状态标签
    $statusLabel = New-Object System.Windows.Forms.ToolStripLabel
    $statusLabel.Text = '就绪'
    $toolStrip.Items.Add($statusLabel) | Out-Null
    
    $previewForm.Controls.Add($toolStrip)
    
    # 创建WebBrowser控件
    $webBrowser = New-Object System.Windows.Forms.WebBrowser
    $webBrowser.Dock = 'Fill'
    $webBrowser.ScriptErrorsSuppressed = $true
    $webBrowser.WebBrowserShortcutsEnabled = $true
    $previewForm.Controls.Add($webBrowser)
    
    # 刷新预览内容
    function Update-Preview {
        try {
            $statusLabel.Text = '正在加载...'
            $toolStrip.Refresh()
            
            # 检查是否包含HTML标签
            if ($htmlContent -match '<[^>]+>') {
                # 包含HTML标签，直接显示
                $webBrowser.DocumentText = $htmlContent
            } else {
                # 纯文本，转换为HTML格式
                $escapedContent = [System.Web.HttpUtility]::HtmlEncode($htmlContent)
                $htmlFormatted = @"
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>HTML预览</title>
    <style>
        body { 
            font-family: 'Microsoft YaHei', Arial, sans-serif; 
            line-height: 1.6; 
            margin: 20px; 
            background-color: #f5f5f5;
        }
        .content {
            background-color: white;
            padding: 20px;
            border-radius: 5px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
            white-space: pre-wrap;
        }
    </style>
</head>
<body>
    <div class="content">$escapedContent</div>
</body>
</html>
"@
                $webBrowser.DocumentText = $htmlFormatted
            }
            
            $statusLabel.Text = '加载完成'
        } catch {
            $statusLabel.Text = "加载失败: $($_.Exception.Message)"
        }
    }
    
    # 绑定事件
    $btnRefresh.add_Click({ Update-Preview })
    $btnOpenInBrowser.add_Click({
        try {
            # 创建临时HTML文件
            $tempFile = [System.IO.Path]::GetTempFileName() + '.html'
            if ($htmlContent -match '<[^>]+>') {
                [System.IO.File]::WriteAllText($tempFile, $htmlContent, [System.Text.Encoding]::UTF8)
            } else {
                $escapedContent = [System.Web.HttpUtility]::HtmlEncode($htmlContent)
                $htmlFormatted = @"
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>HTML预览</title>
    <style>
        body { 
            font-family: 'Microsoft YaHei', Arial, sans-serif; 
            line-height: 1.6; 
            margin: 20px; 
            background-color: #f5f5f5;
        }
        .content {
            background-color: white;
            padding: 20px;
            border-radius: 5px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.1);
            white-space: pre-wrap;
        }
    </style>
</head>
<body>
    <div class="content">$escapedContent</div>
</body>
</html>
"@
                [System.IO.File]::WriteAllText($tempFile, $htmlFormatted, [System.Text.Encoding]::UTF8)
            }
            
            # 在默认浏览器中打开
            Start-Process $tempFile
            
            # 延迟删除临时文件
            Start-Job -ScriptBlock {
                Start-Sleep -Seconds 5
                Remove-Item $using:tempFile -Force -ErrorAction SilentlyContinue
            } | Out-Null
            
        } catch {
            [System.Windows.Forms.MessageBox]::Show("无法在浏览器中打开: $($_.Exception.Message)", '错误', 'OK', 'Error') | Out-Null
        }
    })
    
    # 窗口关闭时清理
    $previewForm.add_FormClosing({
        $webBrowser.Dispose()
    })
    
    # 初始加载
    Update-Preview
    
    # 显示窗口
    $previewForm.ShowDialog($form) | Out-Null
}

# ---------------- Event wiring ----------------
$miNew.add_Click({ New-File })
$miOpen.add_Click({ Open-File })
$miSave.add_Click({ Save-File | Out-Null })
$miSaveAs.add_Click({ SaveAs-File | Out-Null })
$miClose.add_Click({ Close-CurrentDocument })
$miExit.add_Click({ 
    # 简单关闭策略：若当前文档有修改则提示
    $doc = Get-CurrentDoc -tabs $tabs
    if (-not $doc -or (Confirm-DiscardChanges -doc $doc)) { $form.Close() }
})

$miUndo.add_Click({
    $doc = Get-CurrentDoc -tabs $tabs
    if ($doc -and $doc.TextBox.CanUndo) { $doc.TextBox.Undo() }
})
$miCut.add_Click({ $d = Get-CurrentDoc -tabs $tabs; if ($d) { $d.TextBox.Cut() } })
$miCopy.add_Click({ $d = Get-CurrentDoc -tabs $tabs; if ($d) { $d.TextBox.Copy() } })
$miPaste.add_Click({ $d = Get-CurrentDoc -tabs $tabs; if ($d) { $d.TextBox.Paste() } })
$miDelete.add_Click({ $d = Get-CurrentDoc -tabs $tabs; if ($d) { $d.TextBox.SelectedText = '' } })
$miSelectAll.add_Click({ $d = Get-CurrentDoc -tabs $tabs; if ($d) { $d.TextBox.SelectAll() } })
$miTimeDate.add_Click({ $d = Get-CurrentDoc -tabs $tabs; if ($d) { $d.TextBox.SelectedText = [DateTime]::Now.ToString('HH:mm yyyy/M/d') } })

$miWordWrap.add_Click({
    $script:WordWrapEnabled = $miWordWrap.Checked
    # 应用于所有标签页
    foreach ($page in $tabs.TabPages) {
        $doc = $page.Tag
        if ($doc -and $doc.TextBox) {
            $doc.TextBox.WordWrap = $script:WordWrapEnabled
            $doc.TextBox.ScrollBars = if ($script:WordWrapEnabled) { 'Vertical' } else { 'Both' }
        }
    }
})

$miFont.add_Click({
    $doc = Get-CurrentDoc -tabs $tabs
    if (-not $doc) { return }
    $fontDialog.Font = $doc.TextBox.Font
    if ($fontDialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { $doc.TextBox.Font = $fontDialog.Font }
})

$miStatusBar.add_Click({
    $script:ShowStatusBar = $miStatusBar.Checked
    $status.Visible = $script:ShowStatusBar
})

$miApiKey.add_Click({ Show-ApiKeySettings })

$miHtmlPreview.add_Click({ Show-HtmlPreview })

$miAbout.add_Click({
    [System.Windows.Forms.MessageBox]::Show("记事本克隆 - 使用 PowerShell WinForms 构建\n版权所有 © 2025", '关于', 'OK', 'Information') | Out-Null
})

$miAI.add_Click({
    $doc = Get-CurrentDoc -tabs $tabs
    if (-not $doc -or [string]::IsNullOrWhiteSpace($doc.TextBox.Text)) {
        [System.Windows.Forms.MessageBox]::Show("当前没有文本内容", '提示', 'OK', 'Information') | Out-Null
        return
    }

    # 禁用AI菜单，防止重复点击
    $miAI.Enabled = $false
    $script:IsAIProcessing = $true
    
    # 更新状态栏显示加载动画
    Update-StatusBar -tb $doc.TextBox -statusLabel $statusLabel
    
    # 使用Application.DoEvents()保持UI响应
    [System.Windows.Forms.Application]::DoEvents()
    
    try {
        # 调用 DeepSeek API
        $apiUrl = "https://api.deepseek.com/chat/completions"
        $apiKey = $script:ApiKey
        $body = @{
            model = "deepseek-chat";
            messages = @(
                @{ role = "system"; content = "You are a helpful assistant." };
                @{ role = "user"; content = $doc.TextBox.Text }
            );
            stream = $false
        } | ConvertTo-Json -Depth 5

        # 使用WebClient进行更精确的编码控制
        $webClient = New-Object System.Net.WebClient
        $webClient.Headers.Add("Authorization", "Bearer $apiKey")
        $webClient.Headers.Add("Content-Type", "application/json; charset=utf-8")
        $webClient.Encoding = [System.Text.Encoding]::UTF8
        
        $responseBytes = $webClient.UploadData($apiUrl, "POST", [System.Text.Encoding]::UTF8.GetBytes($body))
        $responseJson = [System.Text.Encoding]::UTF8.GetString($responseBytes)
        $response = $responseJson | ConvertFrom-Json
        
        $reply = $response.choices[0].message.content

        # 确保文本正确编码
        $replyBytes = [System.Text.Encoding]::UTF8.GetBytes($reply)
        $reply = [System.Text.Encoding]::UTF8.GetString($replyBytes)

        # 格式化回复文本，优化显示效果
        $formattedReply = Format-ReplyText -text $reply -originalDoc $doc

        # 创建新标签页
        $originalName = if ([string]::IsNullOrWhiteSpace($doc.FilePath)) {
            if ($doc.Title) { $doc.Title } else { '无标题' }
        } else {
            [IO.Path]::GetFileNameWithoutExtension($doc.FilePath)
        }
        $replyTitle = $originalName
        New-Tab -title $replyTitle -text $formattedReply -path $null -encoding ([System.Text.UTF8Encoding]::new($false))
        
        $webClient.Dispose()
        
    } catch {
        $errorMsg = if ($_.Exception.Response) {
            try {
                $reader = New-Object System.IO.StreamReader($_.Exception.Response.GetResponseStream())
                ($reader.ReadToEnd() | ConvertFrom-Json | Select-Object -ExpandProperty error)
            } catch { $_.Exception.Message }
        } else {
            $_.Exception.Message
        }
        [System.Windows.Forms.MessageBox]::Show("AI 处理失败: $errorMsg", '错误', 'OK', 'Error') | Out-Null
    } finally {
        $miAI.Enabled = $true
        $script:IsAIProcessing = $false
        Update-StatusBar -tb $doc.TextBox -statusLabel $statusLabel
    }
})

# Tab change update
$tabs.add_SelectedIndexChanged({
    $doc = Get-CurrentDoc -tabs $tabs
    if ($doc -and $doc.TextBox) {
        Update-StatusBar -tb $doc.TextBox -statusLabel $statusLabel
    }
    Set-FormTitle -form $form -tabs $tabs
})

# Drag & Drop open
$form.AllowDrop = $true
$form.add_DragEnter({ if ($_.Data.GetDataPresent([System.Windows.Forms.DataFormats]::FileDrop)) { $_.Effect = [System.Windows.Forms.DragDropEffects]::Copy } })
$form.add_DragDrop({
    $files = $_.Data.GetData([System.Windows.Forms.DataFormats]::FileDrop)
    if ($files -and $files.Length -gt 0) {
        try {
            $encRef = [ref]([System.Text.Encoding]::UTF8)
            $text = Detect-And-ReadText -path $files[0] -encodingOut $encRef
            New-Tab -title ([IO.Path]::GetFileName($files[0])) -text $text -path $files[0] -encoding $encRef.Value
        } catch {
            [System.Windows.Forms.MessageBox]::Show("无法打开文件: $($_.Exception.Message)", '错误', 'OK', '错误') | Out-Null
        }
    }
})

# Close handler（简化：仅检查当前标签页）
$form.add_FormClosing({
    $doc = Get-CurrentDoc -tabs $tabs
    if ($doc -and -not (Confirm-DiscardChanges -doc $doc)) { $_.Cancel = $true }
})

# Initialize: create an initial empty tab
New-Tab -title '无标题' -text '' -path $null -encoding ([System.Text.Encoding]::UTF8)
# 初始化后刷新一次状态栏（防御空值）
$cur = Get-CurrentDoc -tabs $tabs
if ($cur -and $cur.TextBox) { Update-StatusBar -tb $cur.TextBox -statusLabel $statusLabel }

# Exception handlers and run
[System.Windows.Forms.Application]::add_ThreadException({ param($sender,$e) [System.Windows.Forms.MessageBox]::Show("发生未处理异常: " + $e.Exception.Message, '错误', [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error) })
[AppDomain]::CurrentDomain.add_UnhandledException({ param($sender,$e) $ex=$e.ExceptionObject; [System.Windows.Forms.MessageBox]::Show("发生未处理异常: " + $ex.Message, '错误', [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error) })
try { [System.Windows.Forms.Application]::Run($form) } catch { [System.Windows.Forms.MessageBox]::Show("启动失败: $($_.Exception.Message)", '错误', [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Error) | Out-Null }